﻿#include "node.h"

#include <mysql++/query.h>
#include <mysql++/connection.h>

#include <rapidjson/document.h>
#include <cpptools/out.h>
 
#include "main.h"
#include "PubAcct.pb.h"
#include "PubProtocol.h"

Permission::list_t          Permission::all; // 所有权限
Permission::name_list_t     Permission::allName;
Role::list_t                Role::all; // 所有角色
Account::list_t             Account::all; // 所有在线帐号
Token::list_t               Token::all; // 所有Token

constexpr static const uint64_t kInfo = ct::Out::flagValue((uint64_t)out::kBaseInfo);
constexpr static const uint64_t kErr = ct::Out::flagValue((uint64_t)out::kBaseErr);


// 解析ID字符串,并查找节点,ID存在的存储在set::set列表中, 主要用于解析权限与角色列表
template <typename Node, typename TypeClass, typename Less = std::less<Node>>
void parseIdStr(const char* str, std::set<Node, Less>& nodeList) {
    rapidjson::Document doc;
    Node node;

    if (!doc.Parse(str).HasParseError() && doc.GetType() == rapidjson::kArrayType) {
        for (auto it = doc.Begin(); it != doc.End(); it++) {
            if (it->GetType() == rapidjson::kNumberType) {
                node = TypeClass::find(it->GetUint());
                if (node) {
                    nodeList.insert(node);
                }
            }
        }
    }
}

// ----------------------- Permission相关 -----------------------------
// 载入权限数据(静态，启动时调用)
bool Permission::loadData(void) {
    //auto db = Main::dbpool->grab();
    auto db = Main::dbpool->grab();

    char buf[1024];
    int len = sprintf(buf, "select `id`,`sys`,`name`,`alias` from `%s`;", Main::kTableNamePms);
    auto query = db->query();
    auto res = query.store(buf, len);

    if (!res) {
        fprintf(stderr, "Error --> Load database pms list, err(%d)(%s), sql(%s).\n", query.errnum(), query.error(), buf);
        return false;
    }

    for (auto& row : res) {
        new Permission((unsigned int)row[0], (unsigned int)row[1],
                             string_t(row[2].data(), row[2].size()),
                             string_t(row[3].data(), row[3].size()));
    }

    return true;
}

Permission::~Permission(void) {
    ct::Out::flag(kInfo, "Permission destroy, id(%u), name(%s).\n", id(), name().data());
    all.erase(id()); // 全局列表中移除
    allName.erase(name());

    for (auto& itr : Role::all) { // 所有角色下有此权限删除
        itr.second->pmsDel(this); // 直接删除,无论是否失败
    }

    for (auto& itr : Account::all) { // 所有在线帐号下有此权限删除
        auto sptr = itr.second.lock();
        if (sptr) {
            sptr->pmsDel(this); // 直接删除,无论是否失败
        }
    }
}


// ------------------------ Role相关 ------------------------
// 载入权限数据(静态，启动时调用)
bool Role::loadData(void) {
    enum Idx { kId, kSys, kName, kPms, };
    auto db = Main::dbpool->grab();
    //auto db = Main::dbpool->grab();
    char buf[1024];

    int len = sprintf(buf, "select `id`,`sys`,`name`,`pms` from `%s`;", Main::kTableNameRole);
    auto query = db->query();
    auto res = query.store(buf, len);
    
    if (!res) {
        fprintf(stderr, "Error --> Load database role list, err(%d)(%s), sql(%s).\n", query.errnum(), query.error(), buf);
        return false;
    }

    for (auto& row : res) {
        new Role((unsigned int)row[kId], (unsigned int)row[kSys], 
                 string_t(row[kName].data(), row[kName].size()), row[kPms].data());
    }

    return true;
}

Role::Role(uint32_t id, uint32_t sys, string_t&& name, const char* pmsString)
    : id_(id), sys_(sys), name_(std::forward<string_t>(name)) {
    ct::Out::flag(kInfo, "Role construct, id(%u), name(%s).\n", id, name_.data());

    // 加入全局列表
    all.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(this));

    // 解析权限ID列表
    if (pmsString) { // 可能是空串, API创建的时候已经生成的set结构,无须解析
        parseIdStr<pms_t, Permission, PmsComp>(pmsString, pms_);
    }
}

Role::~Role(void) {
    ct::Out::flag(kInfo, "Role destroy, id(%u), name(%s).\n", id(), name_.data());
    
    all.erase(id()); // 全局列表中移除

    // 所有帐号有此角色则删除
    for (auto& itr : Account::all) { // 所有在线帐号下有此角色删除
        auto sptr = itr.second.lock();
        if (sptr) {
            sptr->role().erase(this); // 直接删除,无论是否失败
        }
    }
}


// ------------------------ Account相关 ---------------------
// 查找帐号是否在列表中, 静态
std::optional<Account::sptr_t> Account::find(const string_t& name) { 
    auto it = all.find(name);

    if (it == all.end()) {
        ct::Out::debug("account %s map not found.\n", name.data());
        return std::nullopt;
    }

    auto sptr = it->second.lock();

    if (!sptr) {
        ct::Out::debug("account %s lock shared_ptr failed, delete it.\n", name.data());
        all.erase(it); // 已失效的wptr删除之
        return std::nullopt;
    }

    return sptr;
}

// 构造
Account::Account(string_t&& name, string_t&& pwd, string_t&& alias, string_t&& image, const char* roleStr,
                 const char* pmsStr, uint8_t teamType, uint32_t team, uint32_t rate, 
                 bool repeat, uint8_t sys, uint8_t sex, uint8_t type)
    : name_(std::forward<string_t>(name)), password_(std::forward<string_t>(pwd)), 
    alias_(std::forward<string_t>(alias)),
    image_(std::forward<string_t>(image)), team_(team), rate_(rate),
    repeat_(repeat), sys_(sys), sex_(sex), type_((Type)type), teamType_(teamType) {
    
    if (!(sys & protect::kAllRole)) { // 非全角色帐号,需要解析角色列表
        parseIdStr<role_t, Role>(roleStr, this->role_);
    }
    if (!(sys & protect::kAllPms)) { // 非全权限帐号,需要解析权限列表
        parseIdStr<pms_t, Permission, PmsComp>(pmsStr, this->pms_);
    }

    ct::Out::flag(kInfo, "Account %s construct.\n", name_.c_str());
}

// 下线所有登陆的token
void Account::tokenDown(uint32_t caugh, uint32_t timesec) {
    std::set<uint64_t>::iterator it;
    uint64_t token;

    // 删除token时会自动从account的tokens_中移除元素,会破坏遍历迭代指针,需要这样循环
    // 一种情况是token的sptr还有引用计数, 并未析构, 不会触发帐号节点下tokens_的删除, 这里得提前删除, 到时候析构时删除会找不到也没关系.
    while ((it = tokens_.begin()) != tokens_.end()) {
        token = *it;
        tokens_.erase(it); // 需要删除, 如果token没析构触发本节点下删除, 就死循环了
        ct::Out::debug("  Offline %s token:%lu, caugh:%u.\n", name_.c_str(), token, caugh);
        auto tokenIt = Token::all.find(token);// 从全局token列表中移除, 此token会析构并从account移除
        if (tokenIt != Token::all.end()) {
            tokenIt->second->lastTime(timesec); // 置下线时间
            tokenIt->second->logoutCaugh(caugh); // 置下线原因
            Token::all.erase(tokenIt); // 大多情况没引用计数会自动析构并从本节点删除
        }
    }
}

// 是否有指定的权限
bool Account::pmsExist(const string_t& name) const {
    if (sys() & protect::kAllPms) { // 有所有权限
        return true;
    }

    auto pmsNode = Permission::find(name); // 根据名字查找节点
    return pmsNode ? pmsExist(pmsNode) : false; // 节点查询重载
}

bool Account::pmsExist(const pms_t pmsNode) const {
    for (auto& itr : role_) { // 先查询角色列表中是否有
        if (itr->pmsExist(pmsNode)) {
            return true;
        }
    }

    // 再查询权限列表
    return pms_.find(pmsNode) != pms_.end(); 
}

void Account::pmsAll(pms_list_t& all) const {
    //printf("pms: sys:%d.\n", (int)sys());
    if (sys() & protect::kAllPms) { // 此帐号为全权限,从全局权限列表中找
        for (auto& itr : Permission::all) { 
            all.insert(itr.second);
        }
        
    } else {
        for (auto& itr : role_) { // 遍历各个角色
            itr->pmsAll(all);
        }
        
        for (auto& itr : pms_) { // 遍历单独权限
            all.insert(itr);
        }
    }
}

// ------------------------ Token相关 ------------------------------
// 查找token是否存在或超时,未超时则更新时间, 静态
std::optional<Token::sptr_t> Token::find(uint64_t id) {
    auto it = all.find(id);
    if (it == all.end()) {
        return std::nullopt;
    }

    auto& tokenSptr = it->second;
    uint32_t currentTimesec = (uint32_t)time(nullptr);
    uint32_t timeoutSec = tokenSptr->lastTime() + tokenSptr->timeout();

    if (timeoutSec < currentTimesec) { // 已超时
        tokenSptr->logoutCaugh(pub::off::kTimeout);
        tokenSptr->lastTime(timeoutSec); // 下线时间置为超时的时间,而不是当前时间
        all.erase(it); // 移除此token
        return std::nullopt;
    } else {
        tokenSptr->lastTime(currentTimesec); // 更新时间
        return tokenSptr;
    }
}

Token::~Token(void) {
    ct::Out::flag(kInfo, "Token %lu destory.\n", token());
    pub::PbAcct pb;
    auto& accSptr = accountSptr();
    pb.set_account(accSptr->name());
    pb.set_on(upTime());
    pb.set_off(lastTime());
    pb.set_team(accSptr->team());
    std::string remark(std::move(ip_)); // ip即将析构,移动赋值效率更高
    switch (caugh_) {
        case pub::off::kReload: remark += "(reload)"; break;
        case pub::off::kLogout: remark += "(logout)"; break;
        case pub::off::kTimeout: remark += "(timeout)"; break;
        case pub::off::kMandatory: remark += "(mandatory)"; break;
        default: break;
    }
    pb.set_remark(remark);
    Main::Main::sendLog(pb, pub::log::kAcct);
    acc_->tokenDel(token()); // 帐号中移除此token值
}